﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace InnovTech.Util.Localization
{
    public class ResxEntity
    {

        //public static readonly string Data = @"data";
        //public static readonly string Data_Value = @"value";
        //public static readonly string Data_Name = @"name";
        //public static readonly string Data_Comment = @"comment";

        private void SetString(ref string old, string current)
        {
            if (current == null || current.Equals(old)) return;
            old = current;
            IsChanged = true;
        }

        private string key;
        public string Key
        {
            get { return key; }
            set 
            {
                SetString(ref key, value);
            }
        }

        private string resourceValue;
        public string ResourceValue
        {
            get
            {
                return resourceValue;
            }
            set
            {
                SetString(ref resourceValue, value);
            }
        }


        private string comment;
        public string Comment
        {
            get
            {
                return comment;
            }
            set
            {
                SetString(ref comment, value);
            }
        }
        public bool IsCommentExists { get { return Comment == null; } }
        
        
        public ResxFileInfo Parent { get; set; }

        public void Update()
        {
            if (IsChanged)
            {
                XmlNode dataNode = GetXmlNode();
                XmlNode valueNode = dataNode.SelectSingleNode(ResxFileInfo.Data_Value);
                valueNode.InnerText = ResourceValue ?? string.Empty;
                XmlNode commentNode = dataNode.SelectSingleNode(ResxFileInfo.Data_Comment);
                if (string.IsNullOrEmpty(Comment))
                {
                    if (commentNode != null)
                    {
                        dataNode.RemoveChild(commentNode);
                    }
                }
                else
                {
                    if (commentNode == null)
                        commentNode = Parent.ResourceXmlDocument.CreateElement(ResxFileInfo.Data_Comment);
                    commentNode.InnerText = Comment;
                    dataNode.AppendChild(commentNode);
                }
                
                IsChanged = false;
            }
        }

        private XmlNode GetXmlNode()
        {
            string xPath = "//" + ResxFileInfo.Data + "[@"+ ResxFileInfo.Data_Name + "='" + Key + "']";
            XmlNode node = Parent.ResourceXmlDocument.DocumentElement.SelectSingleNode(xPath);
            return node;
        }

        private bool isChanged = false;
        public bool IsChanged
        {
            get { return isChanged; }
            set { isChanged = value; }
        }
    }


    public class ResxFileInfo
    {

        public static readonly string Data = @"data";
        public static readonly string Data_Value = @"value";
        public static readonly string Data_Name = @"name";
        public static readonly string Data_Comment = @"comment";

        //private IList<ResxEntity> entities;
        private Dictionary<string, ResxEntity> entities;
        public string FileFullName { get; set; }
        public XmlDocument ResourceXmlDocument { get; set; }

        public Dictionary<string, ResxEntity> Entities
        {
            get
            {
                if (entities == null)
                {
                    entities = new Dictionary<string, ResxEntity>();
                }
                return entities;
            }
        }

        public ResxFileInfo()
        {
            //entities = new Dictionary<string, ResxEntity>();
        }

        public ResxFileInfo(string fileFullName) : this()
        {
            FileFullName = fileFullName;
            Read();
        }

        public void SetValue(string key, string value, string comment)
        {
            ResxEntity entity = GetResourceEntity(key);
            if (entity == null)
            {
                entity = new ResxEntity { Parent = this, Key = key, Comment = comment, ResourceValue = value };
                Entities[entity.Key] = entity;
            }
            else
            {
                entity.Comment = comment;
                entity.ResourceValue = value;
            }
            SetResourceEntity(entity);
        }

        public string GetValue(string key)
        {
            return GetResourceEntity(key).ResourceValue;
        }

        public string GetComment(string key)
        {
            return GetResourceEntity(key).Comment;
        }

        public void SetResourceEntity(ResxEntity entity)
        {
            //TODO
        }

        public ResxEntity GetResourceEntity(string key)
        {
            if (!Entities.ContainsKey(key))
                throw new Exception("no entity with the given key \"" + key + "\"");
            return Entities[key];
        }

        public void Save()
        {
            foreach (ResxEntity entity in Entities.Values)
            {
                entity.Update();
            }
            SaveResxDocument(ResourceXmlDocument, FileFullName);
        }

        public void Read()
        {
            Entities.Clear();
            ResourceXmlDocument = GetResxDocument(FileFullName);
            XmlNodeList dataList = ResourceXmlDocument.GetElementsByTagName(Data);
            foreach (XmlNode dataNode in dataList)
            {
                ResxEntity entity =
                    new ResxEntity {Parent = this, Key = dataNode.Attributes[Data_Name].Value };
                XmlNode valueNode = dataNode.SelectSingleNode(Data_Value);
                entity.ResourceValue = valueNode == null ? null : valueNode.InnerText;
                XmlNode commentNode = dataNode.SelectSingleNode(Data_Comment);
                entity.Comment = commentNode == null ? null : commentNode.InnerText;
                entity.IsChanged = false;
                Entities[entity.Key] = entity;
            }
        }

        public static XmlDocument GetResxDocument(string resxFileFullName)
        {
            using (XmlReader reader = XmlReader.Create(resxFileFullName))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(reader);
                doc.Normalize();
                return doc;
            }
        }

        public static void SaveResxDocument(XmlDocument doc, string resxFileFullName)
        {
            var settings =
                    new XmlWriterSettings
                    {
                        Indent = true,
                        IndentChars = '\t'.ToString(),
                        Encoding = Encoding.UTF8
                    };



            using(var sw = new StreamWriter(resxFileFullName,
                                      false,
                                      new UTF8Encoding(false)))
            using (var write = XmlWriter.Create(sw, settings))
            {
                doc.Save(write);
            }
        }
    }
}
